<?php

namespace app\api\middleware;

use Closure;
use think\facade\Config;
use think\facade\Db;
use think\facade\Cache;
use think\facade\Log;
use think\Request;
use think\Exception;

class RiskControl1
{

//    ACTION_ALLOW	允许	表示该用户行为正常，放行，继续执行后续逻辑（比如登录成功、提现请求继续处理）
//    ACTION_WARN	警告	表示存在轻微风险，不阻止操作，但可记录日志、发送提醒（如：“检测到异常设备，建议修改密码”）
//    ACTION_LIMIT	限制	表示有中等风险，限制部分功能或频率（如：限制1小时内只能提现2次，或限制登录次数）
//    ACTION_BAN	封禁	表示高风险或严重违规，立即阻止操作，甚至封号/封IP（如：模拟器用户直接拒绝登录）
    const ACTION_ALLOW = 'allow';
    const ACTION_WARN  = 'warn';
    const ACTION_LIMIT = 'limit';
    const ACTION_BAN   = 'ban';

    // 日志标签
    const LOG_TAG = 'risk_control';

    /**
     * @var array 缓存的风险标签定义
     */
    private $riskTags = [];

    /**
     * @var array 当前请求上下文
     */
    private $context = [];

    public function handle(Request $request, Closure $next)
    {
        $userId = $request->userId ?? 0;
        $ip = $request->ip();
        $deviceInfo = input('device_info', '', 'trim');

        // 解析设备信息
        $device = [];
        if ($deviceInfo) {
            $decoded = json_decode($deviceInfo, true);
            $device = is_array($decoded) ? $decoded : [];
        }

        // 设置上下文
        $this->context = compact('userId', 'ip', 'device', 'request');

        // === 1. 白名单优先放行 ===
        if ($this->isWhitelisted($ip, $device)) {
            return $next($request);
        }

        // === 2. 加载风险标签定义（缓存）===
        $this->loadRiskTags();

        // === 3. 执行各项风控检查 ===
        $checks = [
            ['name' => 'ip_user_count', 'enabled' => Config::get('risk.check_ip_users', true)], // IP > 3
            ['name' => 'emulator',       'enabled' => Config::get('risk.check_emulator', true)], // 模拟器
            ['name' => 'vpn',            'enabled' => Config::get('risk.check_vpn', true)], // VPN
            ['name' => 'rooted_device',  'enabled' => Config::get('risk.check_rooted', false)], // ROOT
        ];

        foreach ($checks as $check) {
            if (!$check['enabled']) continue;

            $method = 'check' . ucfirst($check['name']);
            if (method_exists($this, $method)) {
                $result = $this->$method($userId, $ip, $device);
                if ($result === self::ACTION_BAN) {
                    $this->logViolation($userId, $ip, $check['name'], 'BANNED');
                    throw new Exception("操作受限，请联系客服", 451);
                } elseif ($result === self::ACTION_LIMIT) {
                    $this->logViolation($userId, $ip, $check['name'], 'LIMITED');
                    throw new Exception("操作频率过高", 429);
                }
            }
        }

        return $next($request);
    }

    // ==================== 检查方法 ====================

    /**
     * 检查同一IP注册用户数
     */
    protected function checkIpUserCount(int $userId, string $ip, array $device): string
    {
        $maxPerIp = Config::get('risk.ip_max_users', 3);

        $cacheKey = "risk:ip_user_count:{$ip}";
        $count = Cache::get($cacheKey);
        if ($count === null) {
            $count = Db::name('user')->where('register_ip', $ip)->count();
            Cache::set($cacheKey, $count, 3600);
        }

        if ($count >= $maxPerIp) {
            $this->applyRiskTag($userId, 'high_ip_risk', ['ip' => $ip, 'count' => $count, 'max' => $maxPerIp]);
            $this->banIp($ip, 'ip_overflow', $count);
            return self::ACTION_BAN;
        }

        return self::ACTION_ALLOW;
    }

    /**
     * 检查是否为模拟器
     */
    protected function checkEmulator(int $userId, string $ip, array $device): string
    {
        if (($device['is_emulator'] ?? 0) == 1) {
            $this->applyRiskTag($userId, 'emulator_user', ['ip' => $ip, 'brand' => $device['brand'] ?? '']);
            $this->banUser($userId, 'emulator');
            return self::ACTION_BAN;
        }
        return self::ACTION_ALLOW;
    }

    /**
     * 检查是否使用VPN/代理
     */
    protected function checkVpn(int $userId, string $ip, array $device): string
    {
        if ($this->isVpnIp($ip)) {
            $this->applyRiskTag($userId, 'proxy_user', ['ip' => $ip]);
            $this->banUser($userId, 'vpn');
            return self::ACTION_BAN;
        }
        return self::ACTION_ALLOW;
    }

    /**
     * 检查是否为ROOT设备
     */
    protected function checkRootedDevice(int $userId, string $ip, array $device): string
    {
        if (($device['is_rooted'] ?? 0) == 1) {
            $this->applyRiskTag($userId, 'rooted_device', ['ip' => $ip, 'model' => $device['model'] ?? '']);
            // 可配置：警告 or 封禁
            if (Config::get('risk.rooted_action') === 'ban') {
                $this->banUser($userId, 'rooted_device');
                return self::ACTION_BAN;
            }
            return self::ACTION_WARN;
        }
        return self::ACTION_ALLOW;
    }

    // ==================== 工具方法 ====================

    /**
     * 判断是否为VPN IP（带缓存）
     */
    private function isVpnIp(string $ip): bool
    {
        $cacheKey = "risk:vpn_ip:{$ip}";
        return Cache::remember($cacheKey, function () use ($ip) {
            return (bool)Db::name('vpn_ip')->where('ip', $ip)->value('id');
        }, 86400);
    }

    /**
     * 打风险标签
     */
    private function applyRiskTag(int $userId, string $tagCode, array $context = [])
    {
        if (!$userId || !isset($this->riskTags[$tagCode])) return;

        $tagId = $this->riskTags[$tagCode]['id'];
        $exists = Db::name('user_risk_tag')
            ->where('user_id', $userId)
            ->where('tag_id', $tagId)
            ->where('expire_time', 'IS', NULL)
            ->find();

        if ($exists) return;

        Db::name('user_risk_tag')->insert([
            'user_id' => $userId,
            'tag_id' => $tagId,
            'source' => 'system',
            'context' => json_encode($context, JSON_UNESCAPED_UNICODE),
            'create_time' => time()
        ]);

        // 触发规则匹配
        event('risk.tag.applied', $userId, $tagCode, $context);
    }

    /**
     * 封禁用户
     */
    private function banUser(int $userId, string $reason): void
    {
        $expireTime = null; // 永久封禁

        Db::name('user_ban')->insert([
            'user_id' => $userId,
            'reason' => $reason,
            'create_time' => time(),
            'expire_time' => $expireTime,
        ]);

        // 清除用户缓存
        Cache::delete("user:{$userId}:profile");
    }

    /**
     * 封禁IP
     */
    private function banIp(string $ip, string $reason, $extra = null)
    {
        Db::name('ip_blacklist')->insert([
            'ip' => $ip,
            'reason' => $reason,
            'is_active' => 1,
            'create_time' => time(),
        ]);
    }

    /**
     * 是否在白名单中
     */
    private function isWhitelisted(string $ip, array $device): bool
    {
        $whitelistIps = Config::get('risk.whitelist_ips', []);
        $whitelistDevices = Config::get('risk.whitelist_devices', []);

        if (in_array($ip, $whitelistIps)) {
            return true;
        }

        $deviceId = $device['device_id'] ?? '';
        if ($deviceId && in_array($deviceId, $whitelistDevices)) {
            return true;
        }

        return false;
    }

    /**
     * 记录风控日志
     */
    private function logViolation(int $userId, string $ip, string $rule, string $action, array $context = [])
    {
        $logData = [
            'user_id' => $userId,
            'ip' => $ip,
            'rule' => $rule,
            'action' => $action,
            'time' => date('Y-m-d H:i:s'),
            'context' => $context,
            'url' => $this->context['request']->url(),
            'method' => $this->context['request']->method()
        ];

        Log::info(self::LOG_TAG, $logData);
    }

    /**
     * 加载风险标签（缓存）
     */
    private function loadRiskTags(): void
    {
        if (!empty($this->riskTags)) return;

        $this->riskTags = Cache::remember('risk:all_tags', function () {
            $tags = Db::name('risk_tag')->select()->toArray();
            $map = [];
            foreach ($tags as $tag) {
                $map[$tag['code']] = $tag;
            }
            return $map;
        }, 3600);
    }
}